<?php
namespace app\api\controller\v1;

use think\Config;
use think\Session;
use app\api\model\v1\Token as TokenModel;
use app\api\model\v1\Account as AccountModel;

/**
 * 公共基类
 * 目前假定一个用户一辆车一个设备
 */
class Base extends \think\Controller
{
    public function _initialize()
    {
        // 【HTTP_USER_AGENT】Dalvik/2.1.0 (Linux; U; Android 5.1; AT001 Build/LMY47D)
        // 【HTTP_USER_AGENT】okhttp/3.8.0
        $this->isAndroid = strpos($this->request->server('HTTP_USER_AGENT'), 'okhttp') !== false;
        $this->isIOS = strpos($this->request->server('HTTP_USER_AGENT'), 'Dalvik') !== false;

        $controller = strtolower($this->request->controller());// 全部小写 v1.account;
        $action = strtolower($this->request->action());// 全部小写 getuserforclouddisk;
        // \Dev::ve([$controller, $action]);

        // 验证（除了 secure 控制器之外） || ($controller === 'v1.doc_manage' && $action ==='index')
        if (!($controller === 'v1.secure' || $controller === 'v1.test' || 
            ($controller === 'v1.device' && $action === 'checkpenandjpush') || 
            ($controller === 'v1.account' && $action === 'getuserforclouddisk') ||
            ($controller === 'v1.docmanage' && $action === 'index'))) {

            $param = $this->jmParameter(['account_id', 'token']);

            // 可以没有 account_id；
            if ($controller === 'v1.account' && ($action === 'code' || $action === 'register' || $action === 'login') ||
                $controller === 'v1.docmanage' && $action === 'app') {
                $validateResult = $this->validate($param,
                    ['token' => 'require']
                );
            } else {
                // account_id 必须
                $validateResult = $this->validate($param,
                    $this->jmSetRule(['token' => 'require']),
                    $this->jmSetMessage()
                );
            }
            if (true !== $validateResult) {
                // 不能用return，因为这方法只是用于初始化；如果想要返回数据，必须echo + exit；
                // return $this->jmReturnJson(4001, $validateResult);
                header('Content-Type:application/json');
                echo json_encode(['errcode'=>4001, 'message'=>$validateResult]);
                exit;
            }

            // 验证该token 是否存在；
            $token = TokenModel::field(['token', 'token_time'])->where('token', $param['token'])->find();
            if (is_null($token)) {
                // return $this->jmReturnJson(4103, 'token不存在');
                header('Content-Type:application/json');
                echo json_encode(['errcode'=>4103, 'message'=>'token无效，请退出后重新登录']);
                exit;
            }
            // 验证该token 是否过期；
            // if ((time() - strtotime($token['token_time'])) > 7200) {
            //     // return $this->jmReturnJson(4104, 'token已过期');
            //     header('Content-Type:application/json');
            //     echo json_encode(['errcode'=>4104, 'message'=>'token已过期']);
            //     exit;
            // }

            // token 合法
            // 验证用户是否存在
            if (!empty($param['account_id'])) {
                $accountModel = AccountModel::field(['id', 'username', 'token'])->find($param['account_id'] * 1);
                if (is_null($accountModel)) {
                    // return $this->jmReturnJson(4099, '用户不存在');
                    header('Content-Type:application/json');
                    echo json_encode(['errcode'=>4099, 'message'=>'用户不存在']);
                    exit;
                }
                $isSuccess = 1;
                // 用户存在，处理 token；
                // 用户的 token 为空，则增加进去；
                if (empty($accountModel->token)) {
                    $accountModel->token = $param['token'];
                    $isSuccess = $accountModel->isUpdate(true)->save();
                    if ($isSuccess === 0) {
                        header('Content-Type:application/json');
                        echo json_encode(['errcode'=>4105, 'message'=>'更新用户 token 失败']);
                        exit;
                    }
                } else if ($accountModel->token !== $param['token']) {
                    // 当前token 和user 表内该用户的token 不一样，
                    // 需要判断二者的生成时间，依据时间区别新旧；
                    // 如果当用户表里的token是旧的，则进行删除，同时还要更新用户的token；
                    
                    $tokenModels = TokenModel::where('token', 'IN', [$accountModel->token, $param['token']])->select();
                    $paramTokenGenerateTime = '';
                    $userTokenGenerateTime = '';

                    // 一定是有两个；
                    // 因为如果只有一个的话：
                    //  参数中的token 不存在，那么上面有判断，不会走到这里；
                    //  user 中的token 不存在，这不可能发生。

                    if ($tokenModels[0]['token'] === $param['token']) {
                        $paramTokenGenerateTime = $tokenModels[0]['token_time'];
                        $userTokenGenerateTime = $tokenModels[1]['token_time'];
                    } else {
                        $paramTokenGenerateTime = $tokenModels[1]['token_time'];
                        $userTokenGenerateTime = $tokenModels[0]['token_time'];
                    }

                    // 参数中的token是新的；
                    if (strtotime($paramTokenGenerateTime) > strtotime($userTokenGenerateTime)) {
                        $userToken = $accountModel->token;                            
                        $accountModel->token = $param['token'];
                        $isSuccess = $accountModel->isUpdate(true)->save();
                        if ($isSuccess === 0) {
                            header('Content-Type:application/json');
                            echo json_encode(['errcode'=>4105, 'message'=>'更新用户 token 失败']);
                            exit;
                        }
                        // user 表token 更新成功再删除 token 表的记录，是否删除成功不需要判断；
                        TokenModel::where('token', $userToken)->delete();
                    } else {
                        // 参数中的token是旧的；
                        header('Content-Type:application/json');
                        echo json_encode(['errcode'=>4106, 'message'=>'token无效，请退出后重新登录']);
                        exit;
                    }
                }
            }
        }

        $this->dbTablePrefix = Config::get('database.prefix');

        // 应该使用 HTTP_HOST（本身已经带有端口），而不是 SERVER_NAME；
        $this->httpDomain = $this->request->server('REQUEST_SCHEME') . '://' . $this->request->server('HTTP_HOST');
         // . ':' . $this->request->server('SERVER_PORT');
        
        if ($this->jmParameter('jm_sql_log') === 'y') {
            \think\Db::listen(function($sql, $time, $explain){
                // 记录SQL
                echo $sql. ' ['.$time.'s]<br />';
                // 查看性能分析结果
                dump($explain);
            });
        }

        $this->jm_test_mode = false;
        if ($this->jmParameter('jm_test_mode') === 'y') {
            $this->jm_test_mode = true;
        } 
    }

    public function jmSetRule($rule=[])
    {
        return array_merge(['account_id|账户ID' => ['require', 'regex'=>'/^[1-9]\d*$/']], $rule);
    }

    public function jmSetMessage($rule=[])
    {
        return array_merge([
            'account_id.regex' => '账户ID错误'
        ], $rule);
    }

    // 接收请求参数；
    // $fieldName：数组，或者字符串（此时第一个字符不应是 ','）；
    public function jmParameter($fieldName, $defaultValue='', $filterFunction='trim,htmlspecialchars')
    {
        $data = [];
        if (is_string($fieldName) && $fieldName !== '') {
            if (strpos($fieldName, ',') === false) {
                return $this->request->param($fieldName, $defaultValue, $filterFunction);
            } else {
                $fieldName = explode(',', $fieldName);
            }
        }

        $length = count($fieldName);
        if ($length > 0) {
            for ($i = 0; $i < $length; $i++) {
                $data[$fieldName[$i]] = $this->jmParameter($fieldName[$i], $defaultValue, $filterFunction);
            }
        }
        return $data;
    }

    public function jmReturnJson($errcode = 200, $message = '', $data = '', $jsonFormat = true)
    {
        $ret = ['errcode'=>$errcode, 'message'=>$message];
        if ($data !== '') {
            $ret['data'] = $data;
        }
        if ($jsonFormat) {
            return json($ret);
        }
        return $ret;
    }

    // 参数2 为 false 时返回数组；
    public function jmException($err, $jsonFormat = true)
    {
        $message = Config::get('ifmode.exception_mode') === 'development' ? '异常：'. $err->getMessage() : '异常';
        return $this->jmReturnJson(4000, $message, '', $jsonFormat);
    }

    public function jmIDInvalid($field = '账户')
    {
        return json([
            'errcode' => 4400,
            'message' => empty($field) ? 'id 无效' : $field .' id 无效'
        ]);
    }

    // 验证 account_id；虽然有时候此功能集成到方法 receiveAndTrans2int 中去了，但此方法还有用到的地方；
    public function jmValidateAccountId(&$accountId)
    {
        try {
            $validateResult = $this->validate(['account_id'=>$accountId], 'Account.account_id');
            if (true !== $validateResult) {
                return ['errcode'=>4001, 'message'=>$validateResult];
            }
            $accountId *= 1;
            return true;
        } catch (\Exception $e) {
            return $this->jmException($e, false);
        }
    }
    
    // 删除指定的key
    public function jmUnset(&$data, $fieldArray=[])
    {
        $length = count($fieldArray);
        if ($length > 0) {
            for ($i = 0; $i < $length; $i++) { 
                unset($data[$fieldArray[$i]]);
            }
        }
    }

    // 删除值为空字符串的 key，因为 $this->request->param() 的默认值设置的是空字符串；
    // $data：一维关联数组；
    public function jmUnsetEmpty(&$data)
    {
        foreach ($data as $key => $value) {
            if ($value === '') {
                unset($data[$key]);
            }
        }
    }

    // 接收请求参数，并把那些应为整型的转为整型；
    public function jmTrans2int(&$data, $intFieldArray=[])
    {
        $length = count($intFieldArray);
        if ($length > 0) {
            for ($i = 0; $i < $length; $i++) { 
                if (isset($data[$intFieldArray[$i]])) {
                    $data[$intFieldArray[$i]] *= 1;
                }
            }
        }
    }

    // 删除值为空字符串的 key，并且把指定的key转为int；上面两个方法的合并；
    public function jmUnsetEmptyAndTrans2int(&$data, $intFieldArray=[])
    {
        $this->jmUnsetEmpty($data);
        $this->jmTrans2int($data, $intFieldArray);
    }

    // 例如把 device_id 转为id，并且总是：如果有 account_id 则转为 user_id；
    public function jmAccountIdAndTrans2id(&$data, $trans2IdField='', $unsetUserId=false)
    {
        if (isset($data['account_id'])) {
            if ($unsetUserId === true) {
                unset($data['account_id']);
            } else {
                $data['user_id'] = 1 * $data['account_id'];
                unset($data['account_id']);
            }
        }
        
        if ($trans2IdField !== '' && isset($data[$trans2IdField])) {
            $data['id'] = 1 * $data[$trans2IdField];
            unset($data[$trans2IdField]);
        }
    }

    public function jmGetIMEI($deviceId) {
        // 本应该从 sn 表查询的，但是这个表是原始表，redis中使用的 imei 是少了前3位和最后1位的。
        return \app\api\model\v1\Device::where('id', $deviceId)->value('imei');
        // return SnModel::getIMEIByDeviceId($deviceId);
    }

    public function jmGetGPS($deviceId) {
        try {
            if (\think\Config::get('ifmode.glhf_fake')) {
                $imei = '12345678901';
            } else {
                $imei = $this->jmGetIMEI($deviceId);
            }

            // 根据设备的 imei 编号查询实时定位数据；
            $redis = \jm\helper\Db::getRedisIns();

            // hashmap 取值
            $device = $redis->hget('devices', $imei);// 值是 string；
            /*
            "{\"device_id\":12345678901,\"device_voltage\":100,\"device_car_id\":\"\",\"device_speed\":0,\"device_direction\":351,\"device_mileage\":12,\"device_latlng\":{\"type\":\"Point\",\"coordinates\":[121.50680884900375,31.030779690039395]},\"device_slatlng\":{\"type\":\"Point\",\"coordinates\":[121.502403,31.032911]},\"device_address\":\"\xe4\xb8\x8a\xe6\xb5\xb7\xe5\xb8\x82\xe9\x97\xb5\xe8\xa1\x8c\xe5\x8c\xba\xe6\xb5\xa6\xe6\xb1\x9f\xe9\x95\x87\xe6\xb0\xb8\xe6\x9d\xb0\xe8\xb7\xaf571\xe5\x8f\xb7\xe6\xb0\xb8\xe5\xba\xb7\xe5\x9f\x8e\xe5\xae\x9d\xe9\x93\xad\xe8\x8e\xb2\xe6\xb5\xa6\xe8\x8b\x91,\xe8\xb7\x9d\xe6\xb0\xb8\xe5\xba\xb7\xe5\x9f\x8e\xe5\xae\x9d\xe9\x93\xad\xe8\x8e\xb2\xe6\xb5\xa6\xe8\x8b\x9187\xe7\xb1\xb3\",\"device_name\":\"\",\"device_adcode\":\"310112\",\"device_loctype\":0,\"device_status\":0,\"device_tracking\":0,\"device_tracking_params\":0,\"device_running_params\":{\"Mod\":0,\"Timer\":null,\"Selector\":\"\"},\"device_will_params\":{\"Mod\":0,\"Timer\":null,\"Selector\":\"\"},\"device_offtime\":0,\"device_statictime\":1552379975,\"device_staticlen\":1,\"device_timertime\":1552622384,\"device_acttime\":1552622384,\"device_oil_status\":0,\"device_loctime\":1552381652,\"device_server_endtime\":0,\"device_activity_time\":0,\"device_activity_latlng\":{\"type\":\"\",\"coordinates\":null},\"device_acc_state\":1,\"device_power_state\":2,\"device_type\":1,\"device_last_tracking\":0,\"device_shipping\":0,\"device_alarm\":-1,\"device_store_state\":0,\"device_online\":1,\"device_cmd_num\":0,\"device_user_id\":\"\"}"

             */
            if ($device !== false) {
                $device = json_decode($device, true);
                return ['errcode'=>200, 'message'=>'获取实时定位数据成功', 'data'=>[
                    // 'device_address' => '上海市静安区江宁路街道昌平路317号昌平大厦,距昌平大厦90米',
                    'device_address' => $device["device_address"],
                    // 'device_latlng' => [121.64903093755, 31.43683943337],
                    'device_latlng' => $device["device_latlng"]["coordinates"],
                    // 'device_loctime' => 1550642953
                    'device_loctime' => $device["device_loctime"]
                    // 'device_loctime_datetime' => date('Y-m-d H:i:s', $v->device_loctime)
                ]];
            }
            return ['errcode'=>200, 'message'=>'该设备暂无数据', 'data'=>null];
        } catch (\Exception $e) {
            return $this->jmException($e, false);
        }
    }

    // 根据 account_id 获取总积分已提取积分
    // 总积分：完成的所有任务的积分之和；
    // 已提取积分：积分提取记录表中的积分之和；
    // 待提取积分：以上二者差值；
    public function jmReward($accountId, $onlyTotalReward = false) {
        try {
            $retData = ['errcode'=>200, 'message'=>'获取积分成功'];
            
            // 总积分
            $totalReward = \app\api\model\v1\Task::where('id', 'IN', function($query) use ($accountId) {
                $query->name('task_user')->where('user_id', $accountId)->where('status', '5')->whereNull('deleted_at')->field('task_id');
            })->sum('reward');
            $retData['data'] = ['totalReward'=>$totalReward];

            if (!$onlyTotalReward) {
                // 已提取积分
                $pickedReward = \think\Db::name('reward_log')->where('user_id', $accountId)->sum('reward');
                $retData['data']['pickedReward'] = $pickedReward;
                // 待提取积分
                $retData['data']['reward'] = $retData['data']['totalReward'] - $retData['data']['pickedReward'];
            }

            return $retData;
        } catch (\Exception $e) {
            return $this->jmException($e, false);
        }
    }
}